﻿using System;
using System.Linq;
using System.Text;
using DevExpress.Xpo;
using DevExpress.ExpressApp;
using System.ComponentModel;
using DevExpress.ExpressApp.DC;
using DevExpress.Data.Filtering;
using DevExpress.Persistent.Base;
using System.Collections.Generic;
using DevExpress.ExpressApp.Model;
using DevExpress.Persistent.BaseImpl;
using DevExpress.Persistent.Validation;
using DevExpress.ExpressApp.ConditionalAppearance;

namespace aztall.Module.BusinessObjects
{
	[DefaultClassOptions, ImageName("BO_Invoice"), NavigationItem("Stock and Pricing")]
	[RuleCriteria("", DefaultContexts.Save, "StartDate <= EndDate", CustomMessageTemplate = "Start Date cannot be later than End Date."), XafDisplayName("Price List")]
	[RuleCombinationOfPropertiesIsUnique("DomainPriceListID", DefaultContexts.Save, "Domain.Oid,PriceListID", CustomMessageTemplate = "Price List ID is existed.")]
	[Appearance("AllCustomer", Enabled = false, Criteria = "AllCustomers", Context = "DetailView", TargetItems = "Customers")]
	[Appearance("AllSku", Enabled = false, Criteria = "AllSkus", Context = "DetailView", TargetItems = "PriceListSkus")]
	public class PriceList : DomainObject
	{
		protected string _PriceListID;
		protected string _PriceListName;
		protected DateTime _StartDate;
		protected DateTime _EndDate;
		protected bool _AllCustomers;
		protected bool _AllSkus;

		public PriceList(Session session)
				: base(session)
		{
		}
		public override void AfterConstruction()
		{
			base.AfterConstruction();
		}
		#region own properties
		[RuleRequiredField(DefaultContexts.Save, SkipNullOrEmptyValues = false)]
		public string PriceListID
		{
			get { return _PriceListID; }
			set { SetPropertyValue(nameof(PriceListID), ref _PriceListID, value); }
		}
		public string PriceListName
		{
			get { return _PriceListName; }
			set { SetPropertyValue(nameof(PriceListName), ref _PriceListName, value); }
		}
		[ImmediatePostData, RuleRequiredField(DefaultContexts.Save, SkipNullOrEmptyValues = false)]
		public DateTime StartDate
		{
			get { return _StartDate; }
			set { SetPropertyValue(nameof(StartDate), ref _StartDate, value); }
		}
		[ImmediatePostData, RuleRequiredField(DefaultContexts.Save, SkipNullOrEmptyValues = false)]
		public DateTime EndDate
		{
			get { return _EndDate; }
			set { SetPropertyValue(nameof(EndDate), ref _EndDate, value); }
		}
		[ImmediatePostData(true)]
		public bool AllCustomers
		{
			get { return _AllCustomers; }
			set { SetPropertyValue(nameof(AllCustomers), ref _AllCustomers, value); }
		}
		[ImmediatePostData(true)]
		public bool AllSkus
		{
			get { return _AllSkus; }
			set { SetPropertyValue(nameof(AllSkus), ref _AllSkus, value); }
		}

		#endregion

		[Association("PriceList_Customer", UseAssociationNameAsIntermediateTableName = true), DevExpress.Xpo.DisplayName("Customers"), VisibleInDetailView(true)]
		[DataSourceCriteria("Domain.Oid = '@This.Domain.Oid' && Status.StatusCode = 'Active'")]
		public XPCollection<Customer> Customers
		{
			get
			{
				return GetCollection<Customer>(nameof(Customers));
			}
		}
		[Association("PriceList_PriceListSku", UseAssociationNameAsIntermediateTableName = true), DevExpress.Xpo.Aggregated, DevExpress.Xpo.DisplayName("Skus"), VisibleInDetailView(true)]
		public XPCollection<PriceListSku> PriceListSkus
		{
			get
			{
				return GetCollection<PriceListSku>(nameof(PriceListSkus));
			}
		}
		public static IList<CustomerSkuPrice> GetCustomerSkuPrice(Session session, DateTime fromDate, DateTime toDate, int CustomerOid, int SkuOid)
		{
			return GetCustomerSkuPrice(session, fromDate, toDate, new int[] { CustomerOid }, new int[] { SkuOid });
		}
		public static IList<CustomerSkuPrice> GetCustomerSkuPrice(Session session, DateTime fromDate, DateTime toDate, IEnumerable<int> CustomerOids, IEnumerable<int> SkuOids)
		{
			var priceLists = new XPCollection<PriceList>(session, GroupOperator.Combine(GroupOperatorType.And, new CriteriaOperator[]
																										 {
																											 GroupOperator.Combine(GroupOperatorType.Or, new ContainsOperator("Customers",  new InOperator("Oid", CustomerOids)), new BinaryOperator("AllCustomers", true)),
																											 GroupOperator.Combine(GroupOperatorType.Or, new ContainsOperator("PriceListSkus", new InOperator("Sku.Oid", SkuOids)), new BinaryOperator("AllSkus", true)),
																											 new BinaryOperator("StartDate", toDate, BinaryOperatorType.LessOrEqual),
																											 new BinaryOperator("EndDate", fromDate, BinaryOperatorType.GreaterOrEqual)
																											}
																										)
																								);

			priceLists.PreFetch("PriceListSkus");

			var Customers = new XPCollection<Customer>(session, new InOperator("Oid", CustomerOids));
			var Skus = new XPCollection<Sku>(session, new InOperator("Oid", SkuOids));
			int Days = (int)(toDate - fromDate).TotalDays + 1;

			var result = (from c in Customers
										from s in Skus
										from d in Enumerable.Range(0, Days)
										let date = fromDate.AddDays(d)
										let pl = priceLists.Where(k => (k.AllCustomers || k.Customers.Any(p => p.Oid == c.Oid)) &&
																									(k.AllSkus || k.PriceListSkus.Any(q => q.Sku.Oid == s.Oid)) &&
																									(date >= k.StartDate) &&
																									(date <= k.EndDate)
																			 )
																			 .OrderByDescending(k => k.StartDate)
																			 .FirstOrDefault()
										let plPrice = (pl == null) ? null : pl.PriceListSkus.Where(c => (pl.AllSkus || c.Sku == s)).FirstOrDefault()
										let unitPrice = (plPrice == null) ? s.StdPrice : plPrice.UnitPrice
										select new CustomerSkuPrice() { Date = date, Customer = c, Sku = s, UnitPrice = unitPrice }
																).ToList();
			return result;

		}
	}
	public class CustomerSkuPrice
	{
		public DateTime Date { get; set; }
		public Customer Customer { get; set; }
		public Sku Sku { get; set; }
		public double UnitPrice { get; set; }
	}
}
